﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using System.Reflection;
using System.Windows.Media.Media3D;

namespace ObjectEditor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private Point _selectionBoxStartPoint;
        private Point _selectionBoxEndPoint;
        private bool _selectionInProgress = false;

        public MainWindow()
        {
            InitializeComponent();
            View3D.CameraInertiaFactor = 0.2;
            View3D.CameraMode = HelixToolkit.Wpf.CameraMode.WalkAround;
            ViewModel.ChangeCameraRequested += ViewModel_ChangeCameraRequested;
            this.Closing += MainWindow_Closing;

            var tabs = ViewModel.GetDynamicTabs();
            foreach (var tab in tabs)
            {
                TabContainer.Items.Add(
                    new TabItem()
                    {
                        Header = tab.TabTitle,
                        Content = tab
                    });
            }
        }

        void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            ViewModel.Dispose();
        }

        void ViewModel_ChangeCameraRequested(object sender, Display3D.CameraChangeEventArgs args)
        {
            if (args.LookAtPosition != null)
            {
                double animationTime = args.LookAtAnimationTimeMs == null ? ViewModel.CameraLookAtAnimationTimeMs : args.LookAtAnimationTimeMs.Value ;
                double distance = args.LookAtDistance == null ? ViewModel.CameraLookAtDistance : args.LookAtDistance.Value ;
                double curDistance = Common.Functions.Distance(args.LookAtPosition.Value, View3D.Camera.Position);

                View3D.LookAt(args.LookAtPosition.Value, curDistance < distance ? curDistance : distance, animationTime);
            }
        }

        public MainWindowViewModel ViewModel
        {
            get { return DataContext as MainWindowViewModel; }
        }

        private void ExitButton_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }

        private void View3D_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //this will begin the selection box creation

            if (_selectionInProgress == true)
            {
                UpdateSelectionRectangle();
                _selectionInProgress = false;
            }

            var pos = Mouse.GetPosition(View3D);
            var margin = SelectionRectangle.Margin;
            margin.Left = pos.X;
            margin.Top = pos.Y;
            SelectionRectangle.Margin = margin;
            SelectionRectangle.Width = 0;
            SelectionRectangle.Height = 0;

            _selectionBoxStartPoint = pos;
            _selectionInProgress = true;
        }

        private void View3D_MouseMove(object sender, MouseEventArgs e)
        {
            if (_selectionInProgress)
            {
                UpdateSelectionRectangle();
            }

            var pt = View3D.FindNearestPoint(e.GetPosition(View3D));
            if (pt.HasValue)
            {
                Cursor3DLocationX.Text = pt.Value.X.ToString("F3");
                Cursor3DLocationY.Text = pt.Value.Y.ToString("F3");
                Cursor3DLocationZ.Text = pt.Value.Z.ToString("F3");
            }
        }

        private void UpdateSelectionRectangle()
        {
            var pos = _selectionBoxEndPoint = Mouse.GetPosition(View3D);
            var width = Math.Abs(_selectionBoxStartPoint.X - pos.X);
            var height = Math.Abs(_selectionBoxStartPoint.Y - pos.Y);

            var margin = SelectionRectangle.Margin;
            if (pos.X < _selectionBoxStartPoint.X)
            {
                margin.Left = pos.X;
            }
            if (pos.Y < _selectionBoxStartPoint.Y)
            {
                margin.Top = pos.Y;
            }

            SelectionRectangle.Width = width;
            SelectionRectangle.Height = height;
            SelectionRectangle.Margin = margin;
        }

        private void HideSelectionRectangle()
        {
            SelectionRectangle.Height = 0;
            SelectionRectangle.Width = 0;
            var margin = SelectionRectangle.Margin;
            margin.Left = 0;
            margin.Top = 0;
            SelectionRectangle.Margin = margin;
            _selectionInProgress = false;
        }

        private bool IsPointInsideSelection(Point3D point, double distanceTolerance)
        {
            double largeX = _selectionBoxEndPoint.X > _selectionBoxStartPoint.X ? _selectionBoxEndPoint.X : _selectionBoxStartPoint.X;
            double smallX = largeX == _selectionBoxEndPoint.X ? _selectionBoxStartPoint.X : _selectionBoxEndPoint.X;
            double largeY = _selectionBoxEndPoint.Y > _selectionBoxStartPoint.Y ? _selectionBoxEndPoint.Y : _selectionBoxStartPoint.Y;
            double smallY = largeY == _selectionBoxEndPoint.Y ? _selectionBoxStartPoint.Y : _selectionBoxEndPoint.Y;

            var p2d = HelixToolkit.Wpf.Viewport3DHelper.Point3DtoPoint2D(View3D.Viewport, point);

            if (p2d.X <= largeX && p2d.X >= smallX &&
                p2d.Y <= largeY && p2d.Y >= smallY)
            {
                if (distanceTolerance > 0)
                {
                    var hitTestPoint = View3D.FindNearestPoint(p2d);
                    if (hitTestPoint != null)
                    {
                        if (Common.Functions.Distance(point, (Point3D)hitTestPoint) <= distanceTolerance)
                        {
                            return true;
                        }
                        else return false;
                    }
                    else return false;
                }
                return true;
            }
            else
            {
                return false;
            } 
        }

        private void View3D_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            bool selectionBox = _selectionInProgress;
            UpdateSelectionRectangle();
            HideSelectionRectangle();

            var pt = View3D.FindNearestPoint(e.GetPosition(View3D));
            if (!pt.HasValue)
            {
                //todo make this a nullable type
                pt = new Point3D(-9999999, -9999999, -9999999);
            }

            if (pt.HasValue)
            {
                Func<Point3D, double, bool> func = null;

                if (selectionBox)
                {
                    func = (Func<Point3D, double, bool>)this.IsPointInsideSelection;
                }

                var info = new AppInformation()
                {
                    SelectedRibbonTab = this.MainWindowRibbon.SelectedItem,
                    World3DSelectionBox = func
                };

                ViewModel.World3DClick((Point3D)pt,info);
            }
        }

        private void View3D_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            var pt = View3D.FindNearestPoint(e.GetPosition(View3D));
            if (!pt.HasValue)
            {
                return;
            }

            Point3D point = (Point3D)pt;

            if (e.ChangedButton == MouseButton.Right)
            {
                View3D.Camera.Position = new Point3D(point.X, point.Y, point.Z + 5);
            }
            else if (e.ChangedButton == MouseButton.Left)
            {
                var info = new AppInformation()
                {
                    SelectedRibbonTab = this.MainWindowRibbon.SelectedItem
                };
             
                ViewModel.World3DClick(point,info);                
            }
        }
        
        private void SelectedObjectPropertyGrid_SelectedObjectChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            if (e.NewValue == null) return;

            if (e.OldValue != null)
            {
                if (e.NewValue.GetType() == e.OldValue.GetType())
                {
                    return;
                }
            }

            var properties = new List<string>();
            var defs = SelectedObjectPropertyGrid.PropertyDefinitions = new Xceed.Wpf.Toolkit.PropertyGrid.PropertyDefinitionCollection();

            var pinfos = e.NewValue.GetType().GetProperties();
            foreach (var info in pinfos)
            {
                var attr = (System.ComponentModel.BrowsableAttribute) (info.GetCustomAttribute(typeof(System.ComponentModel.BrowsableAttribute), false));

                if (attr != null && attr.Browsable == false)
                {
                    continue;
                }
                else
                {
                    properties.Add(info.Name);
                }
            }

            foreach (var p in properties)
            {
                defs.Add(new Xceed.Wpf.Toolkit.PropertyGrid.PropertyDefinition()
                {
                    Name = p
                });
            }
        }

        private void CutMaxXHere_Click_1(object sender, RoutedEventArgs e)
        {
            ViewModel.MaxXCut = View3D.Camera.Position.X;
        }

        private void CutMinXHere_Click_1(object sender, RoutedEventArgs e)
        {
            ViewModel.MinXCut = View3D.Camera.Position.X;
        }

        private void CutMaxYHere_Click_1(object sender, RoutedEventArgs e)
        {
            ViewModel.MaxYCut = View3D.Camera.Position.Y;
        }

        private void CutMinYHere_Click_1(object sender, RoutedEventArgs e)
        {
            ViewModel.MinYCut = View3D.Camera.Position.Y;
        }

        private void CutMaxZHere_Click_1(object sender, RoutedEventArgs e)
        {
            ViewModel.MaxZCut = View3D.Camera.Position.Z;
        }

        private void CutMinZHere_Click_1(object sender, RoutedEventArgs e)
        {
            ViewModel.MinZCut = View3D.Camera.Position.Z;            
        }

        private void EditMultipleObjectsButton_Click_1(object sender, RoutedEventArgs e)
        {
            var window = new Window();
            window.Width = 300;
            window.Content =
                new UserControls.PropertyEditorMultiObject(
                    new UserControls.PropertyEditorMultiObjectViewModel(ViewModel.AllSelectedObjects));
            window.ShowDialog();        
        }
    }
}
